#pragma once
#include "Common.h"
#include <vector>
namespace Simulation {

	/** This class encapsulates the state of all particles of a particle model.
	* All parameters are stored in individual arrays.
	*/
	class ParticleData
	{
	private:
		// Mass
		// If the mass is zero, the particle is static
		std::vector<double> m_masses;
		std::vector<double> m_invMasses;

		// Dynamic state
		std::vector<glm::dvec3> m_x0;
		std::vector<glm::dvec3> m_x;
		std::vector<glm::dvec3> m_v;
		std::vector<glm::dvec3> m_a;
		std::vector<glm::dvec3> m_oldX;
		std::vector<glm::dvec3> m_lastX;

	public:
		FORCE_INLINE ParticleData(void) :
			m_masses(),
			m_invMasses(),
			m_x0(),
			m_x(),
			m_v(),
			m_a(),
			m_oldX(),
			m_lastX()
		{
		}

		FORCE_INLINE ~ParticleData(void)
		{
			m_masses.clear();
			m_invMasses.clear();
			m_x0.clear();
			m_x.clear();
			m_v.clear();
			m_a.clear();
			m_oldX.clear();
			m_lastX.clear();
		}

		FORCE_INLINE void addVertex(const glm::dvec3& vertex)
		{
			m_x0.push_back(vertex);
			m_x.push_back(vertex);
			m_oldX.push_back(vertex);
			m_lastX.push_back(vertex);
			m_masses.push_back(1.0);
			m_invMasses.push_back(1.0);
			m_v.push_back(glm::dvec3(0.0, 0.0, 0.0));
			m_a.push_back(glm::dvec3(0.0, 0.0, 0.0));
		}

		FORCE_INLINE glm::dvec3& getPosition(const unsigned int i)
		{
			return m_x[i];
		}

		FORCE_INLINE const glm::dvec3& getPosition(const unsigned int i) const
		{
			return m_x[i];
		}

		FORCE_INLINE void setPosition(const unsigned int i, const glm::dvec3& pos)
		{
			m_x[i] = pos;
		}

		FORCE_INLINE glm::dvec3& getPosition0(const unsigned int i)
		{
			return m_x0[i];
		}

		FORCE_INLINE const glm::dvec3& getPosition0(const unsigned int i) const
		{
			return m_x0[i];
		}

		FORCE_INLINE void setPosition0(const unsigned int i, const glm::dvec3& pos)
		{
			m_x0[i] = pos;
		}

		FORCE_INLINE glm::dvec3& getLastPosition(const unsigned int i)
		{
			return m_lastX[i];
		}

		FORCE_INLINE const glm::dvec3& getLastPosition(const unsigned int i) const
		{
			return m_lastX[i];
		}

		FORCE_INLINE void setLastPosition(const unsigned int i, const glm::dvec3& pos)
		{
			m_lastX[i] = pos;
		}

		FORCE_INLINE glm::dvec3& getOldPosition(const unsigned int i)
		{
			return m_oldX[i];
		}

		FORCE_INLINE const glm::dvec3& getOldPosition(const unsigned int i) const
		{
			return m_oldX[i];
		}

		FORCE_INLINE void setOldPosition(const unsigned int i, const glm::dvec3& pos)
		{
			m_oldX[i] = pos;
		}

		FORCE_INLINE glm::dvec3& getVelocity(const unsigned int i)
		{
			return m_v[i];
		}

		FORCE_INLINE const glm::dvec3& getVelocity(const unsigned int i) const
		{
			return m_v[i];
		}

		FORCE_INLINE void setVelocity(const unsigned int i, const glm::dvec3& vel)
		{
			m_v[i] = vel;
		}

		FORCE_INLINE glm::dvec3& getAcceleration(const unsigned int i)
		{
			return m_a[i];
		}

		FORCE_INLINE const glm::dvec3& getAcceleration(const unsigned int i) const
		{
			return m_a[i];
		}

		FORCE_INLINE void setAcceleration(const unsigned int i, const glm::dvec3& accel)
		{
			m_a[i] = accel;
		}

		FORCE_INLINE const double getMass(const unsigned int i) const
		{
			return m_masses[i];
		}

		FORCE_INLINE double& getMass(const unsigned int i)
		{
			return m_masses[i];
		}

		FORCE_INLINE void setMass(const unsigned int i, const double mass)
		{
			m_masses[i] = mass;
			if (mass != 0.0)
				m_invMasses[i] = static_cast<double>(1.0) / mass;
			else
				m_invMasses[i] = 0.0;
		}

		FORCE_INLINE const double getInvMass(const unsigned int i) const
		{
			return m_invMasses[i];
		}

		FORCE_INLINE const unsigned int getNumberOfParticles() const
		{
			return (unsigned int)m_x.size();
		}

		FORCE_INLINE const std::vector<glm::dvec3>* getVertices()
		{
			return &m_x;
		}

		/** Resize the array containing the particle data.
		 */
		FORCE_INLINE void resize(const unsigned int newSize)
		{
			m_masses.resize(newSize);
			m_invMasses.resize(newSize);
			m_x0.resize(newSize);
			m_x.resize(newSize);
			m_v.resize(newSize);
			m_a.resize(newSize);
			m_oldX.resize(newSize);
			m_lastX.resize(newSize);
		}

		/** Reserve the array containing the particle data.
		 */
		FORCE_INLINE void reserve(const unsigned int newSize)
		{
			m_masses.reserve(newSize);
			m_invMasses.reserve(newSize);
			m_x0.reserve(newSize);
			m_x.reserve(newSize);
			m_v.reserve(newSize);
			m_a.reserve(newSize);
			m_oldX.reserve(newSize);
			m_lastX.reserve(newSize);
		}

		/** Release the array containing the particle data.
		 */
		FORCE_INLINE void release()
		{
			m_masses.clear();
			m_invMasses.clear();
			m_x0.clear();
			m_x.clear();
			m_v.clear();
			m_a.clear();
			m_oldX.clear();
			m_lastX.clear();
		}

		/** Release the array containing the particle data.
		 */
		FORCE_INLINE unsigned int size() const
		{
			return (unsigned int)m_x.size();
		}
	};
}